﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using ScintillaNet;

namespace ArduinoSketch.Core
{
    public partial class frmDocument : BaseFormContent
    {
        private ProjectFileEntity _projectFile;
        string[] slipSeparator = new string[] { ".", "::", "->" };
        string currentWord = null;
        private List<string> cAutoCompleteList = new List<string>();
        private List<string> autoCompleteList = new List<string>();
        private List<string> tmpAutoCompleteList = new List<string>();
        private List<string> tmp2AutoCompleteList = new List<string>();

        public frmDocument()
        {
            InitializeComponent();
            this.scintilla.Margins[0].Width = 20;
            this.scintilla.ConfigurationManager.Language = "cs";
            this.scintilla.Indentation.SmartIndentType = SmartIndent.CPP;

            this.scintilla.AutoComplete.DropRestOfWord = true;
            this.scintilla.AutoComplete.SingleLineAccept = false;
            this.DefaultAutoCompleate();

            _projectFile = new ProjectFileEntity();
        }

        private void DefaultAutoCompleate()
        {
            scintilla.AutoComplete.List.Clear();
            autoCompleteList.Add("void");
            autoCompleteList.Add("byte");
            autoCompleteList.Add("int");
            autoCompleteList.Add("short");
            autoCompleteList.Add("long");
            autoCompleteList.Add("double");
            autoCompleteList.Add("float");
            autoCompleteList.Add("pinMode");
            autoCompleteList.Add("digitalWrite");
            autoCompleteList.Add("digitalRead");
            autoCompleteList.Add("analogRead");
            autoCompleteList.Add("analogReference");
            autoCompleteList.Add("analogWrite");
            autoCompleteList.Add("delay");
            autoCompleteList.Add("delayMicroseconds");
            autoCompleteList.Add("shiftOut");
        }

        public Scintilla Scintilla
        {
            get{ return scintilla; }
        }

        public ProjectFileEntity ProjectFile
        {
            get { return _projectFile; }
            set { _projectFile = value; }
        }

        private void scintilla_TextInserted(object sender, TextModifiedEventArgs e)
        {
            if (this.TabText.IndexOf("*") == -1)
            {
                this.TabText = this.TabText + " *";
            }
        }


        void UpdateAutoCompleteList()
        {
            cAutoCompleteList.Clear();
            tmpAutoCompleteList.Clear();
            tmp2AutoCompleteList.Clear();
            scintilla.AutoComplete.SingleLineAccept = false;

            tmp2AutoCompleteList.AddRange(autoCompleteList);

            Regex r = new Regex(@"([a-zA-Z_][a-zA-Z0-9_\\.:]*)\s([a-zA-Z_][a-zA-Z0-9_]*)[\s;=\\(]");
            Match m = r.Match(scintilla.Text);
            Dictionary<string, string> vars = new Dictionary<string, string>();

            while (m != null && m.Success)
            {
                string type = m.Groups[1].Value;
                string var = m.Groups[2].Value;

                if (!vars.ContainsKey(var))
                    vars.Add(var, type);
                else
                    vars[var] = type;

                m = m.NextMatch();
            }

            r = new Regex(@"([a-zA-Z_][a-zA-Z0-9_]*)::([a-zA-Z_][a-zA-Z0-9_]*)[\s;=\\(]");
            m = r.Match(scintilla.Text);

            while (m != null && m.Success)
            {
                string a = m.Groups[1].Value;
                string b = m.Groups[2].Value;

                string entry = string.Format("{0}.{1}", a, b);

                if (!tmp2AutoCompleteList.Contains(entry))
                    tmp2AutoCompleteList.Add(entry);

                m = m.NextMatch();
            }

            tmpAutoCompleteList.AddRange(tmp2AutoCompleteList);

            foreach (string var in vars.Keys)
            {
                string[] varTokens = vars[var].Split(slipSeparator, StringSplitOptions.None);
                for (int i = 0; i < tmp2AutoCompleteList.Count; i++)
                {
                    string[] autoCompleteTokens = tmp2AutoCompleteList[i].Split(slipSeparator, StringSplitOptions.None);
                    if (varTokens.Length <= autoCompleteTokens.Length)
                    {
                        bool valid = true;
                        int tokenPos = 0;
                        for (int t = 0; t < varTokens.Length; t++)
                        {
                            if (autoCompleteTokens[t].StartsWith(varTokens[t]))
                                tokenPos = t;
                            else
                            {
                                valid = false;
                                break;
                            }
                        }

                        if (valid)
                        {
                            int lengthDiff = autoCompleteTokens.Length - varTokens.Length + 1;
                            string[] newAutoCEntry = new string[lengthDiff];
                            newAutoCEntry[0] = var;

                            for (int t = varTokens.Length, nt = 1; t < autoCompleteTokens.Length; t++, nt++)
                            {
                                newAutoCEntry[nt] = autoCompleteTokens[t];
                            }

                            string entry = string.Join(".", newAutoCEntry);
                            if (!tmpAutoCompleteList.Contains(entry))
                                tmpAutoCompleteList.Add(entry);
                        }
                    }
                }
            }

            StringBuilder currentWord = new StringBuilder();
            Stack<char> prefix = new Stack<char>();
            List<char> sufix = new List<char>();

            char[] startIgl = new char[] { '(', '[' };
            char[] endIgl = new char[] { ')', ']' };
            int regionCount = 0;

            for (int i = scintilla.Selection.Start - 1; i >= 0 && i < scintilla.Text.Length; i--)
            {
                char cc = scintilla.Text[i];

                if (Utils.Check(cc, endIgl))
                {
                    regionCount++;
                }
                else if (Utils.Check(cc, startIgl))
                {
                    if (regionCount > 0)
                        regionCount--;
                    else
                        break;
                }
                else if (char.IsWhiteSpace(cc))
                    break;
                else
                {
                    if (regionCount == 0)
                        prefix.Push(cc);
                }
            }

            for (int i = scintilla.Selection.Start; i < scintilla.Text.Length; i++)
            {
                char cc = scintilla.Text[i];
                if (char.IsWhiteSpace(cc))
                    break;
                else
                {
                    sufix.Add(cc);
                }
            }

            currentWord.Append(prefix.ToArray());
            currentWord.Append(sufix.ToArray());

            string[] currentWordTokens = currentWord.ToString().Split(slipSeparator, StringSplitOptions.None);

            for (int i = 0; i < tmpAutoCompleteList.Count; i++)
            {

                string[] autoCompleteTokens = tmpAutoCompleteList[i].Split(slipSeparator, StringSplitOptions.None);
                if (currentWordTokens.Length <= autoCompleteTokens.Length)
                {
                    bool valid = true;
                    int tokenPos = 0;
                    for (int t = 0; t < currentWordTokens.Length; t++)
                    {
                        if (autoCompleteTokens[t].StartsWith(currentWordTokens[t]))
                            tokenPos = t;
                        else
                        {
                            valid = false;
                            break;
                        }
                    }

                    if (valid)
                    {
                        if (!cAutoCompleteList.Contains(autoCompleteTokens[tokenPos]))
                            cAutoCompleteList.Add(autoCompleteTokens[tokenPos]);
                    }
                }

            }

            scintilla.AutoComplete.List.Clear();
            scintilla.AutoComplete.List.AddRange(cAutoCompleteList);

            this.currentWord = currentWord.ToString();
        }

        private void scintilla_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.Control == true && e.KeyCode == Keys.Space)
            {
                UpdateAutoCompleteList();
            }
        }

        private void scintilla_KeyUp(object sender, KeyEventArgs e)
        {
            if (scintilla.Selection.Start - 1 >= 0 && e.KeyData != Keys.Escape)
            {
                char cc = scintilla.Text[scintilla.Selection.Start - 1];
                switch (cc)
                {
                    case '.':
                    case ':':
                    case '>':
                        UpdateAutoCompleteList();
                        if (cAutoCompleteList.Count > 0 && !scintilla.AutoComplete.IsActive)
                            scintilla.AutoComplete.Show();
                        break;
                }
            }
        }

        private void scintilla_CharAdded(object sender, CharAddedEventArgs e)
        {
            if (e.Ch == '(')
            {
                this.scintilla.CallTip.Show("Your smart Tooltip functionality", this.scintilla.CurrentPos);
            }

        }
    }
}
